package cn.yi.boot.shiro.security.filter;

import cn.yi.boot.shiro.utils.HttpWebUtils;
import com.alibaba.fastjson.JSON;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import yi.boot.common.response.R;
import yi.boot.common.security.dto.BaseShiroUser;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Objects;

@Component
public class KickoutSessionControlFilter extends AccessControlFilter {

    private final String KICKOUT_CACHE = "KICKOUT_CACHE";

    /**
     * 最大的会话数
     */

    @Value("${shiro.login.maxSession:1}")
    private Integer maxSession = 1;
    @Value("${shiro.login.kickoutUrl}")
    private String kickoutUrl;

    private boolean kickoutAfter = false;//是否提出后者，true，表示提出后面登录的，false表示提出前面登录的
    @Autowired
    private SessionManager sessionManager;
    @Autowired
    private CacheManager cacheManager;

    private Cache<String, Deque<Serializable>> cache;


    private static final String KICKOUT_SESSION = "kickout";

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object o) throws Exception {
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        cache = cacheManager.getCache(KICKOUT_CACHE);
        Subject subject = getSubject(request, response);
        if (!subject.isAuthenticated() && !subject.isRemembered()) {
            return true;
        }
        Session session = subject.getSession();
        BaseShiroUser user = (BaseShiroUser) subject.getPrincipal();
        Serializable sessionId = session.getId();//得到sessionId
        Deque<Serializable> queue;
        String username = user.getUsername();
        queue = cache.get(username);
        if (Objects.isNull(queue)) {
            queue = new LinkedList<>();
        }
        Object kickout = session.getAttribute(KICKOUT_SESSION);
        //如果用户没有sessionId且用户没有被提出，放入队列
        if (!queue.contains(sessionId) && Objects.isNull(kickout)) {
            queue.push(sessionId);
        }
        //当登录的sessionId大于最大的允许回话，开始踢人
        while (queue.size() > maxSession) {
            Serializable kickoutSerializable;
            if (kickoutAfter) {//如果踢出后者
                kickoutSerializable = queue.removeFirst();
            } else {
                kickoutSerializable = queue.removeLast();
            }
            Session kickoutSession = sessionManager.getSession(new DefaultSessionKey(kickoutSerializable));
            if (Objects.nonNull(kickoutSession)) {
                kickoutSession.setAttribute(KICKOUT_SESSION, true);
            }
        }
        cache.put(username, queue);
        if (Objects.nonNull(session.getAttribute(KICKOUT_SESSION))) {
            subject.logout();
            saveRequest(request);
            //如果是Ajax，返回json，如果是其他，直接跳转
            if (HttpWebUtils.isAjax((HttpServletRequest) request)) {
                response.setContentType("application/json;charset=UTF-8");
                response.getWriter().write(JSON.toJSONString(kickoutInfo()));
            } else {
                org.apache.shiro.web.util.WebUtils.issueRedirect(request, response, kickoutUrl);
            }

            return false;
        }
        return true;
    }

    private R kickoutInfo() {
        return R.error("已经被踢出");
    }
}
